iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 22
0
Mobile Development

RxSwift / 30天探索之旅系列 第 22

第 22 天 - RxBlocking & RxTest

  • 分享至 

  • xImage
  •  

RxTest跟RxBlocking是基於RxSwift上,測試Rx-based的library,讓我們分兩天,大概的了解一下RxTest跟RxBlocking,個人覺得,RxBlocking的概念比較單純的,就從RxBlocking先講起吧!

RxBlocking

RxBlocking概念是Observable stream轉換成BlockingObservable,是一種特殊的Observable,blocking就是阻斷的意思,他會阻斷當前的thread,就好像現在我們排隊要進餐廳,會有人把你擋下來幫你量體溫一樣,配合一些RxBlocking的operator,當符合條件時,就會返回結果,下面讓我們看個範例

範例

在有限的序列當中,過濾出小於3的數字

let observable = Observable.of(1, 2, 3, 2, 5).filter { $0 < 3 }
// 1
let result = observable.toBlocking()
// 2
XCTAssertEqual(try result.first(), 1)
// 3
XCTAssertEqual(try result.last(), 2)
// 4
XCTAssertEqual(try result.toArray(), [1, 2, 2])
  1. 把Observable轉換成BlockingObservable
  2. first()是BlockingObservable的operator,可以攔截第一個元素,以範例來說就是數字1
  3. last()是BlockingObservable的operator,可以攔截最後一個元素,所以必須要等到Observable結束才會觸發,以範例來說就是數字2
  4. toArray()是BlockingObservable的operator,當Observable結束後,可以發送過的元素以陣列返回結果,以範例來說就是[1, 2, 2]

優劣

  1. 好處就是簡單好懂
  2. 可用於測試第一個元素或是有限的序列
  3. 使用的情境受侷限,RxBlocking是透過阻斷當前thread方式來獲取元素,當在長時間運行的Observable、異步的或是延遲觸發的情境,對RxBlocking都是同步的,這時就不適合使用RxBlocking
  4. 對RxBlocking沒有時間的概念,他在意的是元素,而不是時間

RxTest

RxBlocking雖然好用,但仍然不夠靈活,於是一些情境下需要RxTest解決問題,RxTest提供TestScheduler,是一種特殊的Scheduler,它只能用於測試,並提供虛擬的時間、模擬的Observer跟模擬的Observable,下面讓我們看一個範例。

首先是測試的宣告、setUp()tearDown()部分

// 1
var scheduler: TestScheduler!

override func setUp() {
    super.setUp()
	  // 2
    scheduler = TestScheduler(initialClock: 0)
}

override func tearDown() {
    super.tearDown()
}
  1. 宣告一個TestScheduler
  2. 初始化TestScheduler,並在虛擬時間(virtual time)為0的時候,建立這個Scheduler,這裡說的虛擬時間是TestScheduler所提供的一種時間,並非現實時間中的1秒或1毫秒,我們可以利用這虛擬時間來達到時間線性的模擬

主要測試的程式

func testRxTestExample() {
    let disposeBag = DisposeBag()
    // 1
    let observer = scheduler.createObserver(Int.self)
    // 2, 3
    let observable = scheduler.createColdObservable([
        Recorded.next(100, 1),
        Recorded.next(200, 2),
        Recorded.next(300, 3),
        Recorded.next(400, 2),
        Recorded.next(500, 1)
    ])

    let filterObservable = observable.filter { $0 < 3 }

    filterObservable.subscribe(observer).disposed(by: disposeBag)
    // 4
    scheduler.start()
    // 5
    let results = observer.events.compactMap { $0.value.element }

    XCTAssertEqual(results, [1, 2, 2, 1])
}
  1. 建立Observer,並表明要接收Int元素,也就是TestableObserver<Int>
  2. 建立Observable,並分別在虛擬時間100, 200, 300, 400, 500分別發送5個數字元素
  3. Recorded是RxTest中定義的struct,它包含一個time(虛擬時間)跟value(實際Element值)變數,在Recorded+Event.swift中對其擴展了.next方法,會把Element轉成Event,這也是createColdObservable或createHotObservable所需要的input
  4. Scheduler啟動,以上所設定跟TestScheduler有關的才會開始運行
  5. TestableObserver包含一個變數events,它是[Recorded<Event<Element>>]型態,也就是所有Observable所.next發送的元素都存在這變數當中,再使用compactMap把陣列降。

ColdObservable & HotObservable

Cold/Hot Observable可以視為Observable的一種屬性差異
Hot Observable表示不管有無訂閱,都會發送元素,也代表一建立後,就會佔用資源,任何訂閱者從中訂閱後,可接收所發出的元素,像是Button或是Subject。
Cold Observable僅在訂閱後,才會開始發送元素,也代表有人訂閱時才會佔用資源,訂閱者在訂閱時可以從第一個元素開始接收,就像是data stream或是跟server請求資料
根據RxSwift: Reactive Programming with Swift所提到,在Rx世界中,通常是在測試時,需要較大的控制權,才會需要去探討到Cold/Hot Observable,不然平常只要使用Rx所提供的Observable即可。


一轉眼就22天了,離30天也不遠了,越到後期越累呀,第一次覺得一個月這麼長...明天會嘗試將昨天的程式加上測試,明天見


上一篇
第 21 天 - TableView + Rx 與範例(下)
下一篇
第 23 天 - RxBlocking & RxTest 範例
系列文
RxSwift / 30天探索之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言